#include "kvcfg.h"
#include "map.h"
#include "list.h"
#include "array.h"
#include "list.h"
#include <assert.h>
#include <string.h>
#include <malloc.h>
#include <ctype.h>

C_CODE_BEGIN


struct kv_element {
	enum kv_type type;
	union {
		const char avalue[ATOM_VALUE_MAX_LEN];
		struct {
			struct map*  m;
			struct list* l; // this list will stores the key/value same order as in the script
			struct array* arr;
		} collection;
	} storage;
	const char* key;
	struct array arrbuffer;
};



EXPORT_FUNC struct kv_element* kv_element_create(  enum kv_type t , const char* name ) {
	// BUGS: Once I have forgot to add one (null terminator) to this memory and it
	// leads to a heap corruption !
	size_t append = name == NULL ? 0 : strlen(name)+1;
	struct kv_element* kve;
	if( append >= NAME_MAX_LEN )
		return NULL;
	kve = malloc(sizeof(struct kv_element) + append*sizeof(char));
	kve->type = t;
	if( name != NULL ) {
		kve->key = ((char*)(kve)+sizeof(struct kv_element));
		strcpy((char*)kve->key,name);
	} else {
		kve->key  = NULL;
	}
	if( t == KV_COLLECTION ) {
		kve->storage.collection.arr = NULL;
		kve->storage.collection.m = NULL;
		kve->storage.collection.l = NULL;
		return kve;
	} else if( t == KV_ATOM ) {
		return kve;
	} else {
		free(kve);
		return NULL;
	}
}

EXPORT_FUNC struct kv_element* kv_element_index( struct kv_element* e, int  i ) {
	struct kv_element** val;
	assert(e->type == KV_COLLECTION);
	if( e->storage.collection.arr != NULL )
		val = (struct kv_element**)array_index_at(e->storage.collection.arr,i);
	else
		return NULL;
	return val == NULL ? NULL : *val;
}

EXPORT_FUNC struct kv_element* kv_element_query( struct kv_element* e, const char* k) {
	void** mret;
	assert(e->type==KV_COLLECTION);
	if( e->storage.collection.m != NULL ) {
		mret = (void**)(map_query(e->storage.collection.m,k,NULL));
	}
	else
		return NULL;
	return mret == NULL ? NULL : *((struct kv_element**)(*mret));
}

EXPORT_FUNC bool kv_element_push_back( struct kv_element* e ,struct kv_element* pe) {
	assert(e->type==KV_COLLECTION);
	if( e->storage.collection.arr == NULL ) {
		e->storage.collection.arr = &(e->arrbuffer);
		array_create(e->storage.collection.arr,10,sizeof(struct kv_element*));
	}
	array_push_back(e->storage.collection.arr,&pe);
	return true;
}

EXPORT_FUNC bool kv_element_insert( struct kv_element* e, const char* k, struct kv_element* pe) {
	struct map_kv mkv;
	void* ret;
	assert(e->type==KV_COLLECTION);
	if( e->storage.collection.m == NULL ) {
		// in map we gonna stored the iterator value which is indeed a void* object
		// however we can also fetch this value by dereferencing this void* pointer
		e->storage.collection.m = map_create( sizeof(void*) , 20 , NULL );
		assert(e->storage.collection.l == NULL);
		e->storage.collection.l = list_create(sizeof(struct kv_element*) , 20 );
	}
	// Insert the object into list at first and make the map stored the iterator
	// of this object in list.
	list_push_back(e->storage.collection.l,&pe);
	mkv.key = k;
	// fetch the iterator(object pointer) from the list
	// and store this pointer inside of the map
	ret = list_back(e->storage.collection.l);
	mkv.val = &ret;
	// we make the key pointer allocated from MAP object 
	// same with the pointer internally inside of object
	// kv_element.This is for memory efficiency 
	ret=map_insert(e->storage.collection.m,&mkv,&(pe->key));
	if( ret == NULL ) {
		// we need to push the object from list as well
		list_pop_back(e->storage.collection.l);
		return false;
	}
	return true;
}

EXPORT_FUNC bool kv_element_set_atomic_value( struct kv_element* e, const char* v) {
	assert( strlen(v)< ATOM_VALUE_MAX_LEN );
	assert( e->type == KV_ATOM );
	strcpy((char*)e->storage.avalue,v);
	return true;
}

struct kv_foreach_arg {
	bool (*f)(const char*,struct kv_element*,void*);
	void* a;
};

static bool _kv_foreach_func ( void* node, void* others ) {
	struct kv_foreach_arg* kfa = (struct kv_foreach_arg*)others;
	struct kv_element* ele = *(struct kv_element**)node;
	assert(ele->key!=NULL);
	return kfa->f(ele->key,ele,kfa->a);
}

EXPORT_FUNC void kv_element_kv_foreach( struct kv_element* e, bool (*func_t)(const char* k,struct kv_element*v,void* arg) , void* arg ) {
	struct kv_foreach_arg a;
	assert(e->type == KV_COLLECTION);
	if( e->storage.collection.l == NULL )
		return;
	a.a = arg;
	a.f= func_t;
	// when perform the kv_foreach function, we need to use LIST
	// as target scanning object since the LIST object can guarantee
	// the order.
	list_until( e->storage.collection.l , _kv_foreach_func , &a );
}

EXPORT_FUNC struct ls_foreach_arg {
	void* a;
	bool (*f)(int,struct kv_element*,void*);
};

static bool _ls_foreach_func(void* obj,int idx,void* others) {
	struct ls_foreach_arg* a = (struct ls_foreach_arg*)others;
	return a->f(idx,*(struct kv_element**)obj,a->a);
}

EXPORT_FUNC void kv_element_ls_foreach( struct kv_element* e, bool (*func_t)(int idx,struct kv_element*v,void* arg),void* arg) {
	struct ls_foreach_arg a;
	assert(e->type == KV_COLLECTION);
	if(e->storage.collection.arr == NULL)
		return;
	a.a = arg;
	a.f = func_t;
	array_until(e->storage.collection.arr,_ls_foreach_func,&a);
}

EXPORT_FUNC const char* kv_element_get_key( struct kv_element* e) {
	return e->key;
}

EXPORT_FUNC const char* kv_element_get_atomic_value( struct kv_element* e ) {
	assert(e->type == KV_ATOM);
	return e->storage.avalue;
}

EXPORT_FUNC enum kv_type kv_element_get_type( struct kv_element* e ) {
	return e->type;
}

EXPORT_FUNC void kv_element_kv_remove( struct kv_element* e, const char* k ) {
	void* obj;
	bool bret;
	void* vret;
	struct kv_element* ele;
	assert(e->type==KV_COLLECTION);
	if( e->storage.collection.m != NULL ) {
		// 1. we find the object and than remove it
		vret = map_query(e->storage.collection.m,k,NULL);
		if( vret == NULL )
			return;
		// since the map stores the pointer of void, therefore the vret should be the pointer
		// which points to the void* ,namely void** !
		obj = *(void**)(vret);
		// after dereferencing , the obj should be the valid list iterator for list.
		// therefore ,the obj should be a valid pointer of struct kv_element* , namely a 
		// pointer points to the struct kv_element*( the object we stores inside of list)
		ele = *(struct kv_element**)obj;
		// 2. remove this object inside of the list since obj is valid iterator 
		list_erase(e->storage.collection.l,obj);
		// 3. remove this object from the map
		bret = map_remove(e->storage.collection.m,k);
		assert(bret == true);
		// 4. delete this single kv_element
		kv_element_destroy(ele);
	}
}

EXPORT_FUNC void kv_element_pop_back( struct kv_element* e) {
	assert(e->type==KV_COLLECTION);
	if(e->storage.collection.arr != NULL) {
		kv_element_destroy( *(struct kv_element**)array_index_at(e->storage.collection.arr,array_get_usesize(e->storage.collection.arr)-1) );
		array_pop_back(e->storage.collection.arr);
	}

}
 

static bool _kv_element_ls_destroy(int idx,struct kv_element*v,void* arg) {
	kv_element_destroy(v);
	return true;
}

static bool _kv_element_kv_destroy( const char* k,struct kv_element*v,void* arg ) {
	kv_element_destroy(v);
	return true;
}

static void _kv_element_destroy_sub( struct kv_element* e ) {
	if(e->type == KV_COLLECTION) {
		if( e->storage.collection.m ) {
			kv_element_kv_foreach(e,_kv_element_kv_destroy,NULL);
			map_destroy(e->storage.collection.m);
			assert(e->storage.collection.l);
			list_destroy(e->storage.collection.l);
		}
		if( e->storage.collection.arr ) {
			kv_element_ls_foreach(e,_kv_element_ls_destroy,NULL);
			array_destroy(e->storage.collection.arr);
		}
	}
}

EXPORT_FUNC void kv_element_destroy( struct kv_element* e ) {
	_kv_element_destroy_sub(e);
	free(e);
}



/* 
 * Script parsing 
 */
/*
 * Script spec:
 * "key":"value";
 * CHA := any-character OR escape character
 * SYMBOL := "CHA*"
 * KEY := SYMBOL
 * VALUE := SYMBOL | COLLECTION
 * COLLECTION := { (KVPAIR)* }
 * KVPAIR := KEY:VALUE;
 * SCRIPT := (KVPAIR)*
 */


/*
 * Given a input string stream,parsing the next symbol inside of the stream 
 */
static int _parse_symbol( const char* input , char* buffer , int* offset , size_t max_len ) {
	int i ;
	int bfi;
	assert( input[0] == '"' );
	for( i = 1 , bfi=0; input[i] && i < (int)max_len ; ++i ) {
		if( input[i] != '"' ) {
			if(input[i] == '$') {
				// escape character
				buffer[bfi++] = input[++i];
			} else {
				buffer[bfi++] = input[i];
			}
		} else {
			assert(offset);
			*offset = i+1;
			buffer[bfi]=0;
			return 1;
		}
	}
	return 0;
}

static void _skip_comments( const char* input , int* offset ) {
	assert(input[0] =='#');
	assert(offset);
	*offset=0;
	while( input[*offset] ) {
		if( input[*offset] == '\n' ) {
			++(*offset);
			return;
		}
		++(*offset);
	}
}
/*
 * Parse collection from the given input stream
 */

static int _parse_collection( const char* input , struct kv_element* e , int* offset ) {
	enum {
		STATES_EXPECT_SYMBOL,
		STATES_EXPECT_ARRAY_ELE,
		STATES_EXPECT_EQ,
		STATES_EXPECT_SYM_OR_COL,
		STATES_EXPECT_END
	};
	int states = STATES_EXPECT_SYMBOL;
	int i =1;
	int off;
	char name[NAME_MAX_LEN];
	char aval[ATOM_VALUE_MAX_LEN];
	int iret;
	struct kv_element* cur_child = NULL;
	assert( input[0] == '{' ); 
	assert( kv_element_get_type(e) == KV_COLLECTION );
	while( input[i] !=0 ) {
		if( input[i] == '"' ) {
			if( states == STATES_EXPECT_SYMBOL ) {
				if( _parse_symbol(input+i,name,&off,NAME_MAX_LEN) == 0 ) {
					break;
				}
				i+=off;
				states = STATES_EXPECT_EQ;
				continue;
			} else if( states == STATES_EXPECT_SYM_OR_COL ) {
				if( _parse_symbol(input+i,aval,&off,ATOM_VALUE_MAX_LEN) ==0 ) {
					break;
				}
				i+=off;
				assert(cur_child == NULL);
				cur_child = kv_element_create( KV_ATOM , NULL );
				iret = kv_element_set_atomic_value(cur_child,aval);
				assert(iret !=0);
				iret = kv_element_insert(e,name,cur_child);
				assert(iret !=0);
				states = STATES_EXPECT_END;
				cur_child = NULL;
				continue;
			} else if( states == STATES_EXPECT_ARRAY_ELE ) {
				if( _parse_symbol(input+i,aval,&off,ATOM_VALUE_MAX_LEN) == 0 ) {
					break;
				}
				i+=off;
				assert(cur_child == NULL);
				cur_child = kv_element_create( KV_ATOM , NULL );
				iret = kv_element_set_atomic_value(cur_child,aval);
				assert(iret!=0);
				iret = kv_element_push_back(e,cur_child);
				assert(iret!=0);
				cur_child = NULL;
				states = STATES_EXPECT_END;
				continue;
			} else {
				break;
			}
		} else if( input[i] == ':' ) {
			if( states == STATES_EXPECT_SYMBOL ) {
				// array object goes here
				states = STATES_EXPECT_ARRAY_ELE;
			} else if( states == STATES_EXPECT_EQ ) {
				states = STATES_EXPECT_SYM_OR_COL;
			} else {
				break;
			}
			++i;
			continue;
		} else if( input[i] == ';' ) {
			if( states == STATES_EXPECT_END ) {
				states = STATES_EXPECT_SYMBOL;
			} else {
				break;
			}
			++i;
			continue;
		} else if( input[i] == '{' ) {
			if( states == STATES_EXPECT_SYM_OR_COL ) {
				assert(cur_child == NULL);
				cur_child = kv_element_create(KV_COLLECTION,NULL);
				if( _parse_collection(input+i,cur_child,&off) ==0 ) {
					break;
				}
				iret = kv_element_insert(e,name,cur_child);
				assert(iret !=0);
				cur_child = NULL;
				i += off;
				states = STATES_EXPECT_END;
				continue;
			} else {
				break;
			}
		} else if( input[i] == '}' ) {
			if( states == STATES_EXPECT_SYMBOL ) {
				++i;
				// finished parsing here
				assert(offset);
				*offset = i;
				return 1;
			} else {
				break;
			}
		} else if( input[i] == '#' ) {
			if( states == STATES_EXPECT_SYMBOL || states == STATES_EXPECT_END ) {
				_skip_comments(input+i,&off);
				i+=off;
				continue;
			} else {
				break;
			}
		} else if( isspace(input[i]) ) {
			++i;
			continue;
		} else {
			break;
		}
	}
	
	/* Error happened */
	_kv_element_destroy_sub(e);
	return 0;
}




EXPORT_FUNC struct kv_element* kv_element_load_buffer( const char* scp ) {
	int i =0;
	int off;
	int iret;
	struct kv_element* kve = NULL, *kve_sub = NULL;
	char name[NAME_MAX_LEN];
	char aval[ATOM_VALUE_MAX_LEN];
	int states;

	enum {
		STATES_EXPECT_END,
		STATES_EXPECT_EQ,
		STATES_EXPECT_START ,
		STATES_EXPECT_SYMBOL
	};
	states = STATES_EXPECT_SYMBOL;


	while( scp[i] ) {
		if( scp[i] == '#' ) {
			_skip_comments(scp+i,&off);
			i+=off;
			continue;
		} else if( scp[i] == '"' ) {
			if( states == STATES_EXPECT_SYMBOL ) {	
				if( _parse_symbol(scp+i,name,&off,NAME_MAX_LEN) == 0 ) {
					break;
				} 
				i+= off;
				states = STATES_EXPECT_EQ;
			} else if( states == STATES_EXPECT_START ) {
				if( _parse_symbol(scp+i,name,&off,ATOM_VALUE_MAX_LEN) ==0 ) {
					break;
				}
				i+=off;
				states = STATES_EXPECT_END;
				kve = kv_element_create(KV_ATOM,name);
				iret = kv_element_set_atomic_value(kve,aval);
				assert(iret !=0);
				continue;
			} else {
				
				break;
			}
		} else if( isspace(scp[i]) ) {
			++i;
			continue;
		} else if( scp[i] == ':' ) {
			if( states == STATES_EXPECT_EQ ) {
				++i;
				states = STATES_EXPECT_START;
				continue;
			} else {
				break;
			}
		} else if( scp[i] == ';' ) {
			if( states == STATES_EXPECT_END )
				return kve;
			else
				break;
		} else if( scp[i] == '{' ) {
			if( states == STATES_EXPECT_START ) {
				assert(kve == NULL);
				kve = kv_element_create(KV_COLLECTION,name);
				if( _parse_collection(scp+i,kve,&off) == 0 ) {
					break;
				} 
				i+=off;
				states = STATES_EXPECT_END;
				continue;
			}
		} else {
			break;
		}
	}

	if(kve !=NULL)
		kv_element_destroy(kve);
	return NULL;
}

EXPORT_FUNC struct kv_element* kv_element_load_script( const char* fn ) {
	FILE* f = fopen(fn,"rb+");
	char stkbuf[2048];
	char* hpbuf = NULL;
	char* content;
	size_t sz;
	size_t sret;
	int curseek;
	struct kv_element* ret;
	if( f == NULL )
		return NULL;

	/*
	 * Get the content length of the binary content of the file
	 */
	curseek = ftell(f);
	fseek(f,0,SEEK_END);
	sz = (size_t)ftell(f);
	fseek(f,curseek,SEEK_SET);
	/*
	 * Determine whether to use stack buffer or not
	 */
	if( sz < 2048 )
		content = stkbuf;
	else {
		content = hpbuf = malloc((sz+1)*sizeof(char));
	}
	sret = fread( content , sizeof(char) , sz , f );
	fclose(f);
	assert( sret <= sz );
	content[sret] = 0;
	/*
	 * Parsing the script content
	 */
	ret = kv_element_load_buffer(content);
	if(hpbuf)
		free(hpbuf);
	return ret;

}


struct kv_element_foreach_printer_arg {
	FILE* f;
	int recrusive;
};

static bool _kv_element_ls_printer( int idx,struct kv_element*v,void* arg ) {
	struct kv_element_foreach_printer_arg* a = (struct kv_element_foreach_printer_arg*)arg;
	int i = 0;
	if( kv_element_get_type(v) == KV_ATOM ) {
		for( i = 0 ; i < a->recrusive ; ++i ) {
			fprintf(a->f,"\t");
		}
		fprintf(a->f,":\"%s\";#Index:%d\n",kv_element_get_atomic_value(v),idx);
	}
	return true;
}

static bool _kv_element_kv_printer( const char* k, struct kv_element*v, void* arg ) {
	struct kv_element_foreach_printer_arg* a = (struct kv_element_foreach_printer_arg*)arg;
	int i = 0;
	if( kv_element_get_type(v) == KV_ATOM ) {
		for( i = 0 ; i < a->recrusive ; ++i ) {
			fprintf(a->f,"\t");
		}
		fprintf(a->f,"\"%s\":\"%s\";\n",kv_element_get_key(v),kv_element_get_atomic_value(v));
	} else {
		for( i = 0 ; i < a->recrusive ; ++i ) {
			fprintf(a->f,"\t");
		}
		fprintf(a->f,"\"%s\":{\n",kv_element_get_key(v));
		++(a->recrusive);
		kv_element_kv_foreach(v,_kv_element_kv_printer,arg);
		kv_element_ls_foreach(v,_kv_element_ls_printer,arg);
		--(a->recrusive);
		for( i = 0 ; i < a->recrusive ; ++i ) {
			fprintf(a->f,"\t");
		}
		fprintf(a->f,"};\n");
	}
	return true;
}



EXPORT_FUNC void kv_element_print( FILE* o, struct kv_element* e) {
	struct kv_element_foreach_printer_arg arg;
	fprintf(o,"#This script dump content is generated automatically!\n");
	fprintf(o,"\"%s\":",kv_element_get_key(e));
	if( kv_element_get_type(e) == KV_ATOM )
		fprintf(o,"\"%s\"\n;",kv_element_get_atomic_value(e));
	else {
		arg.f=o;
		arg.recrusive = 1;
		fprintf(o,"{\n");
		kv_element_kv_foreach(e,_kv_element_kv_printer,&arg);
		kv_element_ls_foreach(e,_kv_element_ls_printer,&arg);
		fprintf(o,"};\n");
	}
	fflush(o);
}

C_CODE_END
